package com.zbzly.reactive.reactiverbac.security;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

@Component
@Slf4j
public class JwtSecurityContextRepository implements ServerSecurityContextRepository {

    @Override
    public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
        return Mono.empty();
    }

    @Override
    public Mono<SecurityContext> load(ServerWebExchange exchange) {
        String path = exchange.getRequest().getPath().toString();
        // 过滤路径
        if ("/login".equals(path)) {
            return Mono.empty();
        }
        String token = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (StringUtils.isNotBlank(token)) {
            return Mono
                    .fromCallable(() -> (String) exchange.getAttributes().get("username"))
                    .flatMap((username) -> {
                        boolean blank = username.isBlank();
                        if (blank) {
                            return Mono.empty();
                        }
                        // 如果对token有足够的安全认可，可以采用无状态凭证策略，将username和authorities放置在token串中解析获取，此处就可以不用查询数据库验证
                        // token能正常解析，表示token有效并对应数据库已知用户
                        return Mono.just(new UsernamePasswordAuthenticationToken(username,
                                        null, List.of(new SimpleGrantedAuthority("ROLE_USER"))))
                                .map(SecurityContextImpl::new);
                    });

        } else {
            return Mono.empty();
        }
    }
}
